package com.piece.core.framework.util.object;

import com.piece.core.framework.exception.ServerException;
import com.piece.core.framework.util.collection.ArrayUtil;
import com.piece.core.framework.util.string.StringUtil;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * Class工具类
 */
@Slf4j
public class ClassUtil {

    public static final Map<Class<?>, Class<?>> WRAPPER_PRIMITIVE_MAP = new ConcurrentHashMap(8);
    public static final Map<Class<?>, Class<?>> PRIMITIVE_WRAPPER_MAP = new ConcurrentHashMap(8);

    static {
        WRAPPER_PRIMITIVE_MAP.put(Boolean.class, Boolean.TYPE);
        WRAPPER_PRIMITIVE_MAP.put(Byte.class, Byte.TYPE);
        WRAPPER_PRIMITIVE_MAP.put(Character.class, Character.TYPE);
        WRAPPER_PRIMITIVE_MAP.put(Double.class, Double.TYPE);
        WRAPPER_PRIMITIVE_MAP.put(Float.class, Float.TYPE);
        WRAPPER_PRIMITIVE_MAP.put(Integer.class, Integer.TYPE);
        WRAPPER_PRIMITIVE_MAP.put(Long.class, Long.TYPE);
        WRAPPER_PRIMITIVE_MAP.put(Short.class, Short.TYPE);
        Iterator var0 = WRAPPER_PRIMITIVE_MAP.entrySet().iterator();

        while(var0.hasNext()) {
            Map.Entry<Class<?>, Class<?>> entry = (Map.Entry)var0.next();
            PRIMITIVE_WRAPPER_MAP.put(entry.getValue(), entry.getKey());
        }
    }

    public static Class<?>[] getClasses(Object... objects) {
        Class<?>[] classes = new Class[objects.length];

        for(int i = 0; i < objects.length; ++i) {
            Object obj = objects[i];
            if (null == obj) {
                classes[i] = Object.class;
            } else {
                classes[i] = obj.getClass();
            }
        }
        return classes;
    }

    /**
     * 获取某包下所有类名
     *
     * @param packageName 包名
     * @param isRecursion 是否遍历子包
     * @return 类的完整名称
     */
    public static List<String> getClassName(String packageName, boolean isRecursion) {
        List<String> classNames = null;
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        String packagePath = packageName.replace(".", "/");

        URL url = loader.getResource(packagePath);
        if (null != url) {
            String protocol = url.getProtocol();
            if (protocol.equals("file")) {
                classNames = getClassNameFromDir(url.getPath(), packageName, isRecursion);
            } else if (protocol.equals("jar")) {
                JarFile jarFile = null;
                try {
                    jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
                } catch (Exception e) {
                    log.error("获取包路径[{}]jar异常：{}", packagePath, e.getMessage());
                }
                if (null != jarFile) {
                    classNames = getClassNameFromJar(jarFile.entries(), packageName, isRecursion);
                }
            }
        }
        return classNames;
    }

    /**
     * 从项目文件获取某包下所有类
     *
     * @param filePath    文件路径
     * @param packageName 包名
     * @param isRecursion 是否遍历子包
     * @return 类的完整名称
     */
    private static List<String> getClassNameFromDir(String filePath, String packageName, boolean isRecursion) {
        List<String> className = new ArrayList<>();
        File file = new File(filePath);
        File[] files = file.listFiles();
        Arrays.stream(files).forEach(child -> {
            if (child.isDirectory() && isRecursion) {
                className.addAll(getClassNameFromDir(child.getPath(), packageName + "." + child.getName(), isRecursion));
            } else {
                String fileName = child.getName();
                if (fileName.endsWith(".class") && !fileName.contains("$")) {
                    className.add(packageName + "." + fileName.replace(".class", ""));
                }
            }
        });
        return className;
    }


    /**
     * 从所有jar中搜索该包，并获取该包下所有类
     *
     * @param jarEntries  Jar实体
     * @param packageName 包名
     * @param isRecursion 是否遍历子包
     * @return
     */
    private static List<String> getClassNameFromJar(Enumeration<JarEntry> jarEntries, String packageName, boolean isRecursion) {
        List<String> classNames = new ArrayList<>();
        while (jarEntries.hasMoreElements()) {
            JarEntry jarEntry = jarEntries.nextElement();
            String name = jarEntry.getName();
            //如果是以/开头的
            if (name.charAt(0) == '/') {
                //获取后面的字符串
                name = name.substring(1);
            }
            //如果前半部分和定义的包名相同
            if (name.startsWith(packageName)) {
                int idx = name.lastIndexOf('/');
                //如果以"/"结尾 是一个包
                if (idx != -1) {
                    //获取包名 把"/"替换成"."
                    packageName = name.substring(0, idx).replace('/', '.');
                }
                //如果可以迭代下去 并且是一个包
                if ((idx != -1) || isRecursion) {
                    //如果是一个.class文件 而且不是目录
                    if (name.endsWith(".class") && !jarEntry.isDirectory()) {
                        //去掉后面的".class" 获取真正的类名
                        classNames.add(name.substring(packageName.length() + 1, name.length() - 6));
                    }
                }
            }
        }
        return classNames;
    }

    /**
     * 获取参数不为空值
     *
     * @param value defaultValue 要判断的value
     * @return value 返回值
     */
    public static <T> T nvl(T value, T defaultValue) {
        return null != value ? value : defaultValue;
    }

    /**
     * 判断一个Collection是否为空， 包含List，Set，Queue
     *
     * @param coll 要判断的Collection
     * @return true：为空 false：非空
     */
    public static boolean isEmpty(Collection<?> coll) {
        return null == coll || coll.isEmpty();
    }

    /**
     * * 判断一个Collection是否非空，包含List，Set，Queue
     *
     * @param coll 要判断的Collection
     * @return true：非空 false：空
     */
    public static boolean isNotEmpty(Collection<?> coll) {
        return !isEmpty(coll);
    }

    /**
     * * 判断一个对象数组是否为空
     *
     * @param objects 要判断的对象数组
     * @return true：为空 false：非空
     */
    public static boolean isEmpty(Object[] objects) {
        return null == objects || (objects.length == 0);
    }

    /**
     * * 判断一个对象数组是否非空
     *
     * @param objects 要判断的对象数组
     * @return true：非空 false：空
     */
    public static boolean isNotEmpty(Object[] objects) {
        return !isEmpty(objects);
    }

    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true：为空 false：非空
     */
    public static boolean isEmpty(Map<?, ?> map) {
        return null == map || map.isEmpty();
    }

    /**
     * * 判断一个Map是否为空
     *
     * @param map 要判断的Map
     * @return true：非空 false：空
     */
    public static boolean isNotEmpty(Map<?, ?> map) {
        return !isEmpty(map);
    }

    /**
     * 判断一个类是JAVA类型还是用户定义类型
     *
     * @param clz 对象类型
     * @return true：是JAVA类型  false：不是JAVA类型
     */
    public static boolean isJavaClass(Class<?> clz) {
        return null != clz && clz.getClassLoader() == null;
    }

    /**
     * 判断一个类是否未基础类或包装类
     */
    public static boolean isWrapClass(Class<?> clz) {
        return null != clz && (clz.isPrimitive()
                || clz == Integer.class || clz == Long.class || clz == Short.class
                || clz == Boolean.class || clz == Byte.class || clz == Float.class
                || clz == Double.class || clz == String.class);
    }

    /**
     * 判断一个类是否是数组类型
     */
    public static boolean isJsonArray(Object object) {
        return object.getClass().isArray() || object instanceof Collection;
    }

    /**
     * 获取对象类型
     */
    public static Class<?> getComponentType(Object object) {
        if (null == object) {
            return null;
        }

        if (ArrayUtil.isArray(object)) {
            return object.getClass().getComponentType();
        }

        if (object instanceof Iterator) {
            Iterator<?> copyObj = (Iterator<?>) object;
            return copyObj.next().getClass();
        }

        return object.getClass();
    }

    /**
     * 判断对象是否为null或空对象, 默认不为空
     *
     * @param object
     */
    public static boolean isEmpty(Object object) {
        if (null == object) {
            return true;
        }
        if (object instanceof String) {
            return StringUtil.isEmpty((String) object);
        }
        if (object instanceof Integer) {
            return null == object || (Integer) object == 0;
        }
        if (object instanceof Long) {
            return null == object || (Long) object == 0;
        }
        if (object.getClass().isArray()) {
            return ((Object[]) object).length > 0;
        }
        if (object instanceof Collection) {
            return ((Collection) object).isEmpty();
        }
        return false;
    }

    /**
     * 获得类的全名，包括包名
     */
    public static String getPackPath(Object object) {
        // 检查用户传入的参数是否为空
        if (null == object) {
            throw new IllegalArgumentException("参数不能为空！");
        }
        // 获得类的全名，包括包名
        String clsName = object.getClass().getName();
        return clsName;
    }

    /**
     * 获取应用资源地址
     */
    public static String getResourcePath() {
        return ClassUtil.class.getResource("/").getPath().substring(1);
    }

    /**
     * 获取类路径
     */
    public static String getResourcePath(Class cls) {
        // 检查用户传入的参数是否为空
        if (null == cls) {
            throw new IllegalArgumentException("参数不能为空！");
        }
        ClassLoader loader = cls.getClassLoader();
        // 获得类的全名，包括包名
        String clsName = cls.getName() + ".class";
        // 获得传入参数所在的包
        Package pack = cls.getPackage();
        String path = "";
        // 如果不是匿名包，将包名转化为路径
        if (null != pack) {
            String packName = pack.getName();
            // 此处简单判定是否是Java基础类库，防止用户传入JDK内置的类库
            if (packName.startsWith("java.") || packName.startsWith("javax.")) {
                throw new IllegalArgumentException("不要传送系统类！");
            }
            // 在类的名称中，去掉包名的部分，获得类的文件名
            clsName = clsName.substring(packName.length() + 1);
            if (packName.indexOf(".") < 0) {
                path = packName + "/";
            } else {
                int start = 0, end = 0;
                end = packName.indexOf(".");
                while (end != -1) {
                    path = path + packName.substring(start, end) + "/";
                    start = end + 1;
                    end = packName.indexOf(".", start);
                }
                path = path + packName.substring(start) + "/";
            }
        }
        // 调用ClassLoader的getResource方法，传入包含路径信息的类文件名
        URL url = loader.getResource(path + clsName);
        // 从URL对象中获取路径信息
        String realPath = url.getPath();
        // 去掉路径信息中的协议名"file:"
        int pos = realPath.indexOf("file:");
        if (pos > -1) {
            realPath = realPath.substring(pos + 5);
        }
        // 去掉路径信息最后包含类文件信息的部分，得到类所在的路径
        pos = realPath.indexOf(path + clsName);
        realPath = realPath.substring(0, pos - 1);
        // 如果类文件被打包到JAR等文件中时，去掉对应的JAR等打包文件名
        if (realPath.endsWith("!")) {
            realPath = realPath.substring(0, realPath.lastIndexOf("/"));
        }
        try {
            // 对中文和空格路径进行解码
            realPath = java.net.URLDecoder.decode(realPath, "utf-8");
        } catch (Exception e) {
            throw new ServerException(e.getMessage());
        }
        return realPath;
    }

    /**
     * 判断是否为静态方法
     */
    public static boolean isStaticMethod(Method method) {
        if (null == method) {
            return false;
        }
        int modifiers = method.getModifiers();
        return Modifier.isStatic(modifiers);
    }

    public static Class<?> wrap(Class<?> clazz) {
        if (null != clazz && clazz.isPrimitive()) {
            Class<?> result = PRIMITIVE_WRAPPER_MAP.get(clazz);
            return null == result ? clazz : result;
        } else {
            return clazz;
        }
    }

    public static Class<?> unWrap(Class<?> clazz) {
        if (null != clazz && !clazz.isPrimitive()) {
            Class<?> result = WRAPPER_PRIMITIVE_MAP.get(clazz);
            return null == result ? clazz : result;
        } else {
            return clazz;
        }
    }

    public static boolean isAllAssignableFrom(Class<?>[] types1, Class<?>[] types2) {
        if (ArrayUtil.isEmpty(types1) && ArrayUtil.isEmpty(types2)) {
            return true;
        } else if (null != types1 && null != types2) {
            if (types1.length != types2.length) {
                return false;
            } else {
                for(int i = 0; i < types1.length; ++i) {
                    Class<?> type1 = types1[i];
                    Class<?> type2 = types2[i];
                    if (isWrapClass(type1) && isWrapClass(type2)) {
                        if (unWrap(type1) != unWrap(type2)) {
                            return false;
                        }
                    } else if (!type1.isAssignableFrom(type2)) {
                        return false;
                    }
                }

                return true;
            }
        } else {
            return false;
        }
    }
}
